//Tranz-Am : Map generator
//Version 9a.
#include "../../../../../axl_framework.h"
#include "test1.h"

using namespace AXL_Projects;
Framework* GameFramework=NULL;		//our axl framework

int MapGame[MapHeight][MapWidth];	//our tile map - this is what we use in the game

BITMAP* Hedge=NULL;
BITMAP* Wall=NULL;
Actions CurrentAction=NONE;			//stuff for drawing the tilemap on the screen, positions of start/end points on map
Actions LastAction=NONE;
int CurrentX=0;
int CurrentY=0;
int StartX=-1;
int EndX=-1;
int StartY=-1;
int EndY=-1;

//initialise allegro and run the program
int main(void)
{
	GameFramework=new Framework("config.xml","");
	if(Configuration::GlobalErrorString!="")
	{
		//some kind of error
		delete GameFramework;
		allegro_message(Configuration::GlobalErrorString.c_str());
	}
	else
	{
		//initialise some stuff first
		if(!InitialiseData())
		{
			delete GameFramework;
			allegro_message(Configuration::GlobalErrorString.c_str());
		}
		else
		{
			//this function invokes the FPS based timer loop. 
			//we do not require ESCape to be defaulted as an escape from the whole system
			//as we are coding that ourselves
			//we only want one logic/drawing function hook and we've assigned it to the 'GAME' enumeration 
			//doesn't matter what we do as the names ar arbitrary
			//The two functions mentioned are in game_game.cpp
			if( !GameFramework->StartGameLoop(
					NULL,NULL,
					NULL,NULL,
					MazeLogic,MazeDraw,
					NULL,NULL,
					false) ) 
			{
				delete GameFramework;
				allegro_message("Failed to run game loop. Check config.log");
			}
			else 
			{
				//endgame will destroy all the stuff we created
				CleanUp();
				//this will destroy all animations and configurations, and shut down allegro
				delete GameFramework;
				
				//any final errors
				if(Configuration::GlobalErrorString!="")
				{
					Configuration::LogEntry(Configuration::GlobalErrorString);
					delete GameFramework;
					allegro_message(Configuration::GlobalErrorString.c_str());
				}
				
			}
		}
	}
	allegro_exit();
	return 0;
}
END_OF_MAIN();

//set up our data
bool InitialiseData()
{
	//bitmaps for our rather simple tilemap
	Hedge=create_bitmap(BlockSize,BlockSize);
	Wall=create_bitmap(BlockSize,BlockSize);

	//green for hedge, red for wall. Both are the same really, just two for variety
	//to show the difference between the tilemap and the binary astar map
	rectfill(Hedge,0,0,Hedge->w,Hedge->h,makecol(0,0,255));
	rectfill(Wall,0,0,Wall->w,Wall->h,makecol(255,0,0));

	//abort if not done properly
	if(!Hedge || !Wall) return false;

	//initialise with an empty map
	ClearGameMap(NONE);

	return true;
}


//free memory from the game - call before exit
void CleanUp()
{
	if(Wall) destroy_bitmap(Wall);
	if(Hedge) destroy_bitmap(Hedge);
}

//simply read the keyboard
bool MazeLogic()
{
	CurrentAction=NONE;
	if(keypressed())
	{
		if(key[KEY_ESC])	return true;
		if(key[KEY_S])		CurrentAction=SETSTART;
		if(key[KEY_E])		CurrentAction=SETEND;
		if(key[KEY_H])		CurrentAction=SETHEDGE;
		if(key[KEY_W])		CurrentAction=SETWALL;
		if(key[KEY_C])		CurrentAction=SETNONE;
		if(key[KEY_A])		CurrentAction=CLEARALL;
		if(key[KEY_M])		CurrentAction=MAZE;
		if(key[KEY_1])		CurrentAction=RANDOM1;
		if(key[KEY_2])		CurrentAction=RANDOM5;
		if(key[KEY_3])		CurrentAction=RANDOM10;
		if(key[KEY_LEFT])	CurrentAction=LEFT;
		if(key[KEY_RIGHT])	CurrentAction=RIGHT;
		if(key[KEY_UP])		CurrentAction=UP;
		if(key[KEY_DOWN])	CurrentAction=DOWN;
		if(key[KEY_4])		CurrentAction=ASTAR1;
		if(key[KEY_5])		CurrentAction=ASTAR2;
		clear_keybuf();
	}
	if(CurrentAction!=NONE) 
		ProcessKeys();

	return false;

}

//handle the keyboard input. probably could be merged with GetKeys()
//but looks rather fetching like this
void ProcessKeys()
{
	if(CurrentAction==LastAction) 
		return;	//avoid holding down key, well a slight pause

	switch(CurrentAction)
	{
	case LEFT:
		CurrentX--;
		break;
	case RIGHT:
		CurrentX++;
		break;
	case UP:
		CurrentY--;
		break;
	case DOWN:
		CurrentY++;
		break;
	case SETSTART:	//clear the existing start location and set new one
		if(StartX!=-1)
			MapGame[StartY][StartX]=0;
		StartX=CurrentX;
		StartY=CurrentY;
		MapGame[StartY][StartX]=0;
		break;
	case SETEND:	//clear the existing end location and set new one
		if(EndX!=-1)
			MapGame[EndY][EndX]=0;
		EndX=CurrentX;
		EndY=CurrentY;
		MapGame[EndY][EndX]=0;
		break;
	case SETHEDGE:
		MapGame[CurrentY][CurrentX]=MAP_HEDGE;
		break;
	case SETWALL:
		MapGame[CurrentY][CurrentX]=MAP_WALL;
		break;
	case SETNONE:
		MapGame[CurrentY][CurrentX]=0;
		break;
	//the these below are to create a new tile map
	case CLEARALL:
		ClearGameMap(NONE);		//wipe everything
		break;
	case MAZE:
		ClearGameMap(MAZE);		//create a sexy maze
		break;
	case RANDOM1:
		ClearGameMap(RANDOM1);	//populate with 1% coverage of hedges/walls
		break;
	case RANDOM5:
		ClearGameMap(RANDOM5);	//as random1 but 5%
		break;
	case RANDOM10:
		ClearGameMap(RANDOM10);	//as random1 but 5%
		break;
	default:
		break;
	}

	//loop around the edges
	if(CurrentX<0) CurrentX=MapWidth-1;
	if(CurrentY<0) CurrentY=MapHeight-1;
	if(CurrentX>=MapWidth) CurrentX=0;
	if(CurrentY>=MapHeight) CurrentY=0;

}

//dump everything to the screen
bool MazeDraw()
{
	//clear the screen and draw a rectangle around our map
	clear(GameFramework->DrawingSurface);
	rect(GameFramework->DrawingSurface,XOffset-1,YOffset-1,XOffset+BlockSize*MapWidth+1,YOffset+BlockSize*MapHeight+1,makecol(128,128,128));

	//draw the menu
	textout_ex(GameFramework->DrawingSurface,font,"Esc: Exit",StartMenu,10,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"S  : Set Start",StartMenu,20,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"E  : Set End",StartMenu,30,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"H  : Draw Hedge",StartMenu,40,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"W  : Draw Wall",StartMenu,50,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"C  : Clear",StartMenu,60,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"A  : Clear All",StartMenu,70,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"M  : Draw Maze",StartMenu,80,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"1  : Draw Rnd 20%",StartMenu,90,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"2  : Draw Rnd 50%",StartMenu,100,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"3  : Draw Rnd 80%",StartMenu,110,makecol(0,255,0),-1);
	//draw our tiles
	for(int i=0;i<MapHeight;i++)
		for(int j=0;j<MapWidth;j++)
		{
			if(MapGame[i][j]==MAP_WALL)
				draw_sprite(GameFramework->DrawingSurface,Wall,XOffset+(j*BlockSize),YOffset+(i*BlockSize));
			if(MapGame[i][j]==MAP_HEDGE)
				draw_sprite(GameFramework->DrawingSurface,Hedge,XOffset+(j*BlockSize),YOffset+(i*BlockSize));
		}

	//draw the start/end locations
	textout_ex(GameFramework->DrawingSurface,font,"S",XOffset+(StartX*BlockSize)+3,YOffset+(StartY*BlockSize)+3,makecol(0,255,0),-1);
	textout_ex(GameFramework->DrawingSurface,font,"E",XOffset+(EndX*BlockSize)+3,YOffset+(EndY*BlockSize)+3,makecol(0,255,0),-1);

	//draw rectangle around the current square
	rect(GameFramework->DrawingSurface,XOffset+(CurrentX*BlockSize),YOffset+(CurrentY*BlockSize),
		XOffset+(CurrentX*BlockSize)+BlockSize,YOffset+(CurrentY*BlockSize)+BlockSize,
		makecol(0,255,0));

	return false;
}

//our tilemap is a grid. each grid contains either a 0 for an empty tile, 1 for a hedge, 2 for a wall
//in our tilemap both are exactly the same, i.e. they block the route

//this is for clearing our tilemap or filling it with a pattern of blocks
void ClearGameMap(Actions a)
{
	//user wants our tilemap to be a maze
	if(a==MAZE)
		GenerateMaze();	//draw a nice maze and set start/end points automatically
	else
	{
		StartY=StartX=EndX=EndY=-1;
		for(int i=0;i<MapHeight;i++)
			for(int j=0;j<MapWidth;j++)
			{
				MapGame[i][j]=0;	//clear each tile first
				//get a random number, if it fits our range then set the tile to be a wall or hedge (1 or 2)
				if(a==RANDOM1)
				{
					if(rand()%100<1) MapGame[i][j]=rand()%2+1;
				}
				if(a==RANDOM5)
				{
					if(rand()%100<5) MapGame[i][j]=rand()%2+1;
				}
				if(a==RANDOM10)
				{
					if(rand()%100<10) MapGame[i][j]=rand()%2+1;
				}
			}
	}
}

//user wishes to create a maze, generate it using the maze generator code, which is half this file below!
void GenerateMaze()
{
	//create a grid using our tile map height/width and pass in a map for populating with
	//the original code used it's own internal map, but because ours is so simple
	//we don't really need anything else
	Grid mymaze(MapWidth,MapHeight,MapGame[0]);
	mymaze.GenerateMaze();	//generate a new maze

	//set the correct start/end points. our maze always generates start/end at the edge
	StartX=0;
	EndX=MapWidth-1;
	StartY=mymaze.GetStartY();
	EndY=mymaze.GetGoalY();
	MapGame[StartY][StartX]=0;
	MapGame[EndY][EndX]=0;
}

void MessageBox(char* message)
{
	rectfill(screen,220,200,420,300,makecol(255,0,0));
	textout_ex(screen,font,message,240,240,makecol(0,0,0),-1);
	textout_ex(screen,font,"PRESS SPACE",240,260,makecol(0,0,0),-1);
	clear_keybuf();
	while(!key[KEY_SPACE]) 
		rest(20);
}

//------------------------------------------------------------------------------
// Desc: maze
// Created: 05 December 2002 12:58:41
// (c)2002 Neil Wakefield
//------------------------------------------------------------------------------

// Desc: Constructor for the game grid object
Grid::Grid(int w, int h, int* map)
{
	m_solution.clear();
	mapw=w;
	maph=h;
	m_tiles=map;
}

// Desc: Generates a maze using a path-growing algorithm
void Grid::GenerateMaze()
{
	//no random equals the same mazes :)
	//uncomment and include time.h if you want different

	//srand( unsigned int( time( NULL ) ) );

	vector< MazeSquare > path;	//list of all path squares so far

	bool foundExit = false; //have we found an exit

	//fill the entire grid with wall squares
	for( int y = 0; y < maph; ++y )
	{
		for( int x = 0; x < mapw; ++x )
		{
			SetTile( x, y, 1 );
		}
	}

	//pick a random starting point in the left hand column
	m_startY = rand() % mapw;
	SetTile( 0, m_startY, 1 );

	//record this square
	MazeSquare square;
	square.y = m_startY;
	square.x = 0;
	path.push_back( square );

	//now grow the paths from here
	while( 1 )
	{
		if( 0 == path.size() )
		{
			//no more elements left to expand from, this shouldn't actually happen
			break;
		}

		//take a random square from the path list
		vector< MazeSquare >::iterator currentSquare = path.begin();
		advance( currentSquare, rand() % int( path.size() ) );

		square = *currentSquare;
		int x = square.x;
		int y = square.y;

		//pick a random direction to move in from this square
		Direction d = Direction( rand() % 4 );
		//int newD = int( d );

		//see if this square is valid
		MoveInDirection( x, y, d );
		if( ! CanPlacePath( x, y ) )
		{
			//try next direction
			x = square.x;
			y = square.y;
			int newd = int( d );
			++newd; newd %= 4; d = Direction( newd );
			MoveInDirection( x, y, d );
			
			if( ! CanPlacePath( x, y ) )
			{
				//try next direction
				x = square.x;
				y = square.y;
				++newd; newd %= 4; d = Direction( newd );
				MoveInDirection( x, y, d );
				
				if( ! CanPlacePath( x, y ) )
				{
					//try next direction
					x = square.x;
					y = square.y;
					++newd; newd %= 4; d = Direction( newd );
					MoveInDirection( x, y, d );
					if( ! CanPlacePath( x, y ) )
					{
						//all four directions tried, so this path is useless
						//remove it from the places-to-try list
						path.erase( currentSquare );
						continue;
					}
				}
			}
		}

		//if we get this far, x,y should contain the location of the next sqaure to
		// add to the path
		SetTile( x, y, 0 );
		square.x = x;
		square.y = y;
		path.push_back( square );

		//see if this path is a valid exit
		if( x == ( mapw - 1 ) )
		{
			//if we haven't already found an exit, use this one
			if( ! foundExit )
			{
				//set this tile to the goal
				SetTile( x, y, 1 );
				m_goalY = y;

				foundExit = true;

				//the maze is complete, but continue until all possible squares have been
				//used so we don't end up with large areas of wall
			}
		}
	}
}

// Desc: Checks to see if putting a path at a given point would break the maze
bool Grid::CanPlacePath( const int x, const int y )
{
	//make sure the square is inside the bounds of the grid
	if( ( x < 0 ) || ( x >= mapw ) ||
		( y < 0 ) || ( y >= maph ) )
		return false;

	//check to see if this square is already a path
	if( 0 == GetTile( x, y ) )
		return false;

	//check to see if this square would cause a loop
	// this would happen if the square was touching two or more path squares
	bool foundOneSquare = false;

	//track paths found - we need these to constrain diagonals
	bool foundLeft = false;
	bool foundRight = false;
	bool foundUp = false;
	bool foundDown = false;

	bool minX = ( x <= 0 );
	bool maxX = ( x >= ( mapw - 1 ) );
	bool minY = ( y <= 0 );
	bool maxY = ( y >= ( maph - 1 ) );

	//up
	if( !minY )
		if( 0 == GetTile( x, y - 1 ) )
		{
			foundOneSquare = true;
			foundUp = true;
		}

	//right
	if( !maxX )
		if( 0 == GetTile( x + 1, y ) )
			if( foundOneSquare )
				return false;
			else
			{
				foundOneSquare = true;
				foundRight = true;
			}
		
	//down
	if( !maxY )
		if( 0 == GetTile( x, y + 1 ) )
			if( foundOneSquare )
				return false;
			else
			{
				foundOneSquare = true;
				foundDown = true;
			}

	//left
	if( !minX )
		if( 0 == GetTile( x - 1, y ) )
			if( foundOneSquare )
				return false;
			else
			{
				foundOneSquare = true;
				foundLeft = true;
			}

	//add some constraints to creating diagonals as this results in ugly mazes
	//upper-left
	if( !minY && !minX )
		if( 0 == GetTile( x - 1, y - 1 ) )
			if( ! foundLeft && ! foundUp )
				return false;
			else
				foundOneSquare = true;

	//upper-right
	if( !minY && !maxX )
		if( 0 == GetTile( x + 1, y - 1 ) )
			if( ! foundRight && ! foundUp )
				return false;
			else
				foundOneSquare = true;

	//lower-right
	if( !maxY && !maxX )
		if( 0 == GetTile( x + 1, y + 1 ) )
			if( ! foundRight && ! foundDown )
				return false;
			else
				foundOneSquare = true;

	//lower-left
	if( !minX && !maxY )
		if( 0 == GetTile( x - 1, y + 1 ) )
			if( ! foundLeft && ! foundDown )
				return false;
			else
				foundOneSquare = true;

	//must be a valid place to put a path
	return true;
}

// Desc: Moves along the grid one unit in the specified direction
void Grid::MoveInDirection( int& x, int& y, const Direction d )
{
	if( d == DIR_UP )
		y++;
	else if( d == DIR_DOWN )
		y--;
	else if( d == DIR_LEFT )
		x--;
	else if( d == DIR_RIGHT )
		x++;
}
